package com.gzq.singleton.exam00;

import org.junit.Test;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class TestSingleton {

    /**
     * 饿汉式 —— 测试静态常量版单例。
     */
    @Test
    public void testStaticConstant() {
        // 第一次获取单例
        EhStaticConstant s1 = EhStaticConstant.getSingleton();
        System.out.println(s1.hashCode());

        // 第二次获取单例
        EhStaticConstant s2 = EhStaticConstant.getSingleton();
        System.out.println(s2.hashCode());

        //  反射入侵单例模式。
        try {
            Constructor<EhStaticConstant> declaredConstructor
                    = EhStaticConstant.class.getDeclaredConstructor();
            declaredConstructor.setAccessible(true);
            EhStaticConstant s3 = declaredConstructor.newInstance();
            System.out.println(s3.hashCode());
        } catch (NoSuchMethodException | InvocationTargetException | InstantiationException
                | IllegalAccessException e) {
            e.printStackTrace();
        }

        // 序列化入侵单例模式。
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(s2);

            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            EhStaticConstant s4 = (EhStaticConstant) ois.readObject();
            System.out.println(s4.hashCode());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 饿汉式 —— 测试静态代码块版单例。
     */
    @Test
    public void testStaticCodeBlock() {
        // 第一次获取单例
        EhStaticCodeBlock s1 = EhStaticCodeBlock.getSingleton();
        System.out.println(s1.hashCode());

        // 第二次获取单例
        EhStaticCodeBlock s2 = EhStaticCodeBlock.getSingleton();
        System.out.println(s2.hashCode());

        //  反射入侵单例模式。
        try {
            Constructor<EhStaticCodeBlock> declaredConstructor
                    = EhStaticCodeBlock.class.getDeclaredConstructor();
            declaredConstructor.setAccessible(true);
            EhStaticCodeBlock s3 = declaredConstructor.newInstance();
            System.out.println(s3.hashCode());
        } catch (NoSuchMethodException | InvocationTargetException | InstantiationException
                | IllegalAccessException e) {
            e.printStackTrace();
        }

        // 序列化入侵单例模式。
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(s2);

            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            EhStaticCodeBlock s4 = (EhStaticCodeBlock) ois.readObject();
            System.out.println(s4.hashCode());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 懒汉式 —— 方法锁版本。
     */
    @Test
    public void testLhMethodLock() {
        // 第一次获取单例
        LhMethodLock s1 = LhMethodLock.getSingleton();
        System.out.println(s1.hashCode());

        // 第二次获取单例
        LhMethodLock s2 = LhMethodLock.getSingleton();
        System.out.println(s2.hashCode());

        //  反射入侵单例模式。
        try {
            Constructor<LhMethodLock> declaredConstructor
                    = LhMethodLock.class.getDeclaredConstructor();
            declaredConstructor.setAccessible(true);
            LhMethodLock s3 = declaredConstructor.newInstance();
            System.out.println(s3.hashCode());
        } catch (NoSuchMethodException | InvocationTargetException | InstantiationException
                | IllegalAccessException e) {
            e.printStackTrace();
        }

        // 序列化入侵单例模式。
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(s2);

            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            LhMethodLock s4 = (LhMethodLock) ois.readObject();
            System.out.println(s4.hashCode());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 懒汉式 —— 双重校验锁单例。
     */
    @Test
    public void testLhDoubleCheckLock() {
        // 第一次获取单例
        LhDoubleCheckLock s1 = LhDoubleCheckLock.getSingleton();
        System.out.println(s1.hashCode());

        // 第二次获取单例
        LhDoubleCheckLock s2 = LhDoubleCheckLock.getSingleton();
        System.out.println(s2.hashCode());

        //  反射入侵单例模式。
        try {
            Constructor<LhDoubleCheckLock> declaredConstructor
                    = LhDoubleCheckLock.class.getDeclaredConstructor();
            declaredConstructor.setAccessible(true);
            LhDoubleCheckLock s3 = declaredConstructor.newInstance();
            System.out.println(s3.hashCode());
        } catch (NoSuchMethodException | InvocationTargetException | InstantiationException
                | IllegalAccessException e) {
            e.printStackTrace();
        }

        // 序列化入侵单例模式。
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(s2);

            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            LhDoubleCheckLock s4 = (LhDoubleCheckLock) ois.readObject();
            System.out.println(s4.hashCode());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }


    /**
     * 懒汉式 —— 双重校验锁单例。
     */
    @Test
    public void testLhStaticInnerClass() {
        // 第一次获取单例
        System.out.println("第一次获取单例");
        LhStaticInnerClass s1 = LhStaticInnerClass.getSingleton();
        System.out.println(s1.hashCode());

        // 第二次获取单例
        LhStaticInnerClass s2 = LhStaticInnerClass.getSingleton();
        System.out.println(s2.hashCode());

        //  反射入侵单例模式。
        try {
            Constructor<LhStaticInnerClass> declaredConstructor
                    = LhStaticInnerClass.class.getDeclaredConstructor();
            declaredConstructor.setAccessible(true);
            LhStaticInnerClass s3 = declaredConstructor.newInstance();
            System.out.println(s3.hashCode());
        } catch (NoSuchMethodException | InvocationTargetException | InstantiationException
                | IllegalAccessException e) {
            e.printStackTrace();
        }

        // 序列化入侵单例模式。
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(s2);

            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            LhStaticInnerClass s4 = (LhStaticInnerClass) ois.readObject();
            System.out.println(s4.hashCode());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 懒汉式——枚举单例模式。
     */
    @Test
    public void testLhEnum() {
        LhEnum s1 = LhEnum.SINGLETON;
        System.out.println(s1.hashCode());

        LhEnum s2 = LhEnum.SINGLETON;
        System.out.println(s2.hashCode());

        // 反射入侵枚举单例模式。无效。
        try {
            Constructor<LhEnum> declaredConstructor = LhEnum.class.getDeclaredConstructor();
            LhEnum lhEnum = declaredConstructor.newInstance();
            System.out.println(lhEnum.hashCode());
        } catch (NoSuchMethodException | InvocationTargetException
                | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }

        // 序列化入侵枚举单例模式。 无效。
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(s2);
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            LhEnum s4 = (LhEnum) ois.readObject();
            System.out.println(s4.hashCode());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
